home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 4
/
Aminet 4 - November 1994.iso
/
aminet
/
comm
/
misc
/
avmnfaxsrc1_33.lha
/
efax0.6a.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-03
|
66KB
|
2,163 lines
#define Copyright "Copyright 1994 Ed Casas"
#define Version "efax v 0.6a"
/*
Copyright (C) 1994 Ed Casas
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
You may contact the author by e-mail at: edc@ee.ubc.ca,
or by mail at: 2629 West 3rd Ave, Vancouver, BC, Canada,
V6K 1M4.
*/
const char *Usage =
"Usage:\n"
" %s [ option ]... [ -r pat | -t num file... ]\n"
"Options:\n"
" -c cap set file format or receive capabilites to cap\n"
" -d dev use modem on device dev\n"
" -g cmd exec \"/bin/sh -c cmd\" for data calls\n"
" -i str send modem command ATstr at start\n"
" -l id set local indetification to id\n"
" -o opt use protocol option opt:\n"
" 1 use class 1 modem commands\n"
" a if first [data mode] answer attempt fails retry as fax\n"
" e ignore errors in modem initialization commands\n"
" r reverse bit order on receive\n"
" x use XON instead of DC2 to trigger reception\n"
" z add 100 ms to pause before each modem comand (cumulative)\n"
" -q ne ask for retransmission if more than ne errors per page\n"
" -s share (unlock) modem device while waiting for call\n"
" -v lvl print messages of type in string lvl (ewinchamr)\n"
" -w don't answer phone, wait for OK or CONNECT instead\n"
" -x fil use uucp-style lock file fil\n"
" -z str send modem command ATstr when done\n"
"Commands:\n"
" -r answer and receive fax into files pat.001, pat.002, ... \n"
" -t send fax image files file... to telephone num\n"
;
#include <ctype.h> /* ANSI C */
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h> /* POSIX */
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h> /* select() */
#ifndef FD_SET
#include <select.h> /* for AIX */
#endif
#include <sys/stat.h> /* for SysV (?) */
#ifdef TERMIO
#include <termio.h>
#include <sys/ioctl.h>
#define termios termio
#define tcgetattr(fd, pt) ioctl(fd, TCGETA, pt)
#define tcsetattr(fd, x, pt) ioctl(fd, TCSETAW, pt)
#define cfsetospeed(pt, b) ((pt)->c_cflag = ((pt)->c_cflag & ~CBAUD) | b)
#define cfsetispeed(pt, b)
#define tcdrain(fd)
#else
#include <termios.h>
#endif
#ifndef FILENAME_MAX
#define FILENAME_MAX 255
#endif
#ifndef u_char
#define u_char unsigned char
#endif
/* constants... */
#define FAXFILE "/dev/fax" /* default fax modem device */
#define LOGF stderr /* session log written to this stream */
/* delays/timeouts, in deciseconds */
#define TO_RESET 26 /* timeout for modem reset commands (per Hayes) */
#define T_CMD 1 /* pause before each modem command */
#define TO_A 1200 /* dial/answer (Phase A) - modem may T.O. first */
#define TO_DATAF 150 /* software adaptive answer data connect */
#define T1 350 /* T.30 T1 - waiting for DIS/DCS before Phase B */
#define T2 60 /* T.30 T2 - waiting for frame in Phase B */
#define T3S 30 /* T.30 response timeout (not T3) */
#define T4 30 /* T.30 T4 - between [re]transmissions of DIS */
#define TO_DRAIN 140 /* drain 4k modem buffer at 2400bps (tx) */
#define TO_RTCMD 10 /* return to command mode after DLE-ETX (rx) */
#define TO_FT 31 /* max delay after +F[TR][MH] command */
#define TO_CHAR 51 /* per data character (max FILL length) */
#define TO_ABRT 20 /* max delay after sending abort sequence */
#define TO_C2B 450 /* Class 2 DIS to CONNECT:(DCS+TCF+CFR)xretries */
#define TO_C2X 20 /* Class 2 wait for XON: 2/5 of 5s timeout */
#define TO_C2PP 200 /* Class 2 wait for ppr: (ppm+ppr)x3retries + 2 */
#define TO_C2R 600 /* Class 2 receive: (TCF+FTT)x11 retrains + 5 */
#define TO_C2EOR 60 /* Class 2 end of data rx (2 retrans x 3 s) */
#define CMDBUFSIZE 128 /* longest possible modem command or response */
#define DEFDISLEN 3 /* length of DIS initially transmitted */
#define DEFCAP 1,3,0,2,0,0,0,0 /* default local capabilities */
#define DLE_ETX "\020\003" /* DLE-ETX (end of data) string */
#define FNAMFT "%m%d%H%M%S" /* strftime() format for default file name */
#define HDBLKFLAG '#' /* prefix to force HDB (text) lock file style */
#define IDLEN 20 /* length of T.30 identification strings */
#define LOCKPOLLDELAY 15 /* seconds between checks of lock files */
#define MAXDIS 8 /* maximum DIS frames sent without response (T1) */
#define MAXFIFLEN 125 /* max FIF len = MAXFRLEN - (adx+ctl+FCF) - FCS */
#define MAXFRLEN 130 /* max frame length = 3.45s x 300 bps / 8 */
#define MAXGETTY 512 /* maximum length of ``getty'' (-g) command */
#define MAXIOPT 100 /* maximum # of modem initialization commands */
#define MAXLKFILE 16 /* maximum number of lock files */
#define MAXMSGBUF 8192 /* maximum size of message buffer */
#define MAXNULLS 2 /* maximum consecutive received nulls saved */
#define MAXTSTAMP 80 /* maximum length of a time stamp */
#define MAXTRAINERR 0 /* maximum errors allowed in training check data */
#define MAXTRAIN 2 /* maximum training retries at lowest speed */
#define MAXRETRY 3 /* maximum retries of unacknowledged commands */
#define MINWRITE 128 /* minimum bytes per write() to modem */
#define NCAP 8 /* number of fields in a capability string */
#define NTXRETRY 3 /* maximum re-sends per page */
#define RCVBUFSIZE 1024 /* read up to this many bytes at a time from fax */
#define SNDBUFSIZE 1024 /* maximum bytes to write at a time to fax */
#define T4RTCLEN 9 /* end of page mark for T.4 coded images */
#define T4RTC "\000\020\001\000\020\001\000\020\001"
enum cchar { /* control characters */
NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF,
VT, FF, CR, SO, SI, DLE, XON, DC2, XOFF,DC4, NAK,
SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, US } ;
const char *prompts[] = { /* modem responses that are prompts */
"OOK", "-CONNECT FAX", "CCONNECT", "NNO CARRIER", "EERROR",
"NNO DIALTONE", "BBUSY", "NNO ANSWER", "E+FCERROR", 0 } ;
enum promptcodes { /* codes for modem prompts */
BUSY = 'B', CONNECT = 'C', OK = 'O', RING = 'R', NO = 'N',
ERROR = 'E' } ;
enum ttymodes /* serial port modes */
{ COMMAND, DROPDTR, SEND, ORIGINAL } ;
/* signals to be caught so can hang up phone */
const int catch [] = { SIGHUP, SIGINT, SIGQUIT, SIGIOT, SIGSEGV, SIGALRM,
SIGTERM, SIGFPE, 0 } ;
typedef int cap [ NCAP ] ; /* remote/local capabilities */
/* capability fields... */
enum captype { VR, BR, WD, LN, DF, EC, BF, ST } ;
const int capmax [ NCAP ] = { 1, 7, 3, 2, 3, 2, 1, 7 } ;
/* & maximum values */
/* characters per second for br */
const int cps [ 8 ] = { 300, 600, 900, 1200, 1500, 1800, 900, 1200 } ;
/* next br = fallback [ br ] */
const int fallback [ 8 ] = { 0, 0, 1, 2, 7, 4, 3, 6 } ;
/* minimum scan time in ms */
const int delay [ 8 ] = { 0 , 5, 10, 10, 20, 20, 40, 40 } ;
/* Table to convert between T.30 DIS/DCS/DTC FIF and Class 2-like
capability codes. Uses br=6, 7 for V.17 at 7200, 9600. */
typedef const struct t30tabstruct
{
char *name ;
u_char byte, shift, mask ;
u_char captodis[8], distocap[16], captodcs[8], dcstocap[16] ;
} t30tabst ;
#define X 0xff /* invalid values */
t30tabst t30tab [ NCAP ] = {
{ "vr", 1, 1, 0x01, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
{ "br", 1, 2, 0x0f,
{ 0, 4, 12, 12, 13, 13 } ,
{ 0, X, X, X, 1, X, X, X, 3, X, X, X, 3, 5, 3, X } ,
{ 0, 4, 12, 8, 5, 1 } ,
{ 0, 5, 5, X, 1, 4, 4, X, 3, 7, X, X, 2, 6, X, X } } ,
{ "wd", 2, 6, 0x03, { 0, 2, 1 } , { 0, 2, 1, 2 } ,
{ 0, 2, 1 } , { 0, 2, 1, 2 } },
{ "ln", 2, 4, 0x03, { 0, 2, 1 } , { 0, 2, 1, X } ,
{ 0, 2, 1 } , { 0, 2, 1, X } },
{ "df", 1, 0, 0x01, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
{ "ec", 3, 4, 0x03, { 0, 2, 2 } , { 0, X, 2, X } ,
{ 0, 3, 2 } , { 0, 0, 2, 1 } },
{ "bf", 5, 5, 0x01, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
{ "st", 2, 1, 0x07,
{ 7, 4, 3, 2, 6, 0, 5, 1 } , { 5, 7, 3, 2, 1, 6, 4, 0 } ,
{ 7, 4, X, 2, X, 0, X, 1 } , { 5, 7, 3, 1, X, X, X, 0 } }
} ;
/* values of capability fields */
const char *capvaluestr [ NCAP ] [8] = {
{ " 98lpi", "196lpi" } ,
{ " 2400bps", " 4800bps", " 7200bps", " 9600bps", " 12kbps", "14.4kbps",
"7200bpsV.17", "9600bpsV.17" } ,
{ "8.5\"/215mm", " 10\"/255mm", " 12\"/303mm" } ,
{ "11\"/A4", "14\"/B4", " any " } ,
{ "1D" , "2D" }, { " - ", "ECM-256", "ECM-64 " }, { " - ", "BFT" },
{ "0ms", "5ms", "10/5ms", "10ms", "20/10ms", "20ms", "40/20ms", "40ms" }
} ;
/* T.30 control frames */
enum frametype {
DIS=0x01, CSI, NSF=0x04,
CFR=0x21, FTT,
MCF=0x31, RTN, RTP, PIN, PIP,
DCS=0x41, TSI, NSS=0x44,
CRP=0x58, DCN=0x5f,
EOM=0x71, MPS, EOP=0x074, PRI_EOM=0x79, PRI_MPS, PRI_EOP=0x7c,
DTC=0x80, CIG, NSC=0x84
} ;
/* Class 1 commands to [receive=0/transmit=1] [data=0/training=1] for
[baud rate=BR]. */
const char *c1cmd [ 2 ] [ 2 ] [ 8 ] = {
{ { "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=122", "+FRM=146" ,
"+FRM=74", "+FRM=98" } ,
{ "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=121", "+FRM=145" ,
"+FRM=73", "+FRM=97" } } ,
{ { "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=122", "+FTM=146" ,
"+FTM=74", "+FTM=98", } ,
{ "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=121", "+FTM=145" ,
"+FTM=73", "+FTM=97" } }
} ;
/* World's fastest T.4 decoder. Implements a tree search. Each bit of
each byte is tested using the T4TST(byte,bitmask) macro. This macro
invokes the macro T4CODE(len) when it detects a valid T.4 code or on the
first EOL after an invalid code. The macro's parameter len will be the
run length, -1 on a normal EOL or -2 for the EOL following an error. 1D
coding only.
As per T.4, run lengths >63 (make-up codes) should be added to the
subsequent (terminating) code. The first run of a line is white and
colors alternate.
Each node of the tree contains pointers to the next node for a '0' or a
'1' bit. NULL pointers indicate terminal nodes in which case, the run
length (or -1 or -2) is given by the zlen or olen member and the search
continues with the node given by the znext or onext pointer.
The decoding tree, stored in packed format (LSB to MSB plus guard bit),
is built by calling mktree() which returns a pointer to the initial
value of 't4p' to be used by the T4TST macro.
*/
typedef struct dtree {
struct dtree *zero, *one, *onext, *znext ;
short int zlen, olen ;
} dtree ;
#define T4TST(c,m) t4p = ( c & m ) ? \
( t4p->one ? t4p->one : ( T4CODE ( t4p->olen ), t4p->onext ) ) : \
( t4p->zero ? t4p->zero : ( T4CODE ( t4p->zlen ), t4p->znext ) ) ;
#define T4CTST(c) T4TST(c,0x80) ; T4TST(c,0x40) ; \
T4TST(c,0x20) ; T4TST(c,0x10) ; \
T4TST(c,0x08) ; T4TST(c,0x04) ; \
T4TST(c,0x02) ; T4TST(c,0x01) ;
#define T4NODES 228
const short int pkdt4tree [] = {
7,1,29,53,243,263,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,
27,1,27,2,-2,31,49,43,33,75,35,37,4,10,121,39,41,173,4,63,4,0,
79,45,95,47,93,4,1,71,51,167,4,2,55,61,57,59,4,3,67,69,4,4,63,65,
4,5,83,4,6,4,7,2,128,4,8,4,9,87,73,109,4,11,89,77,99,4,12,117,
103,81,107,4,13,85,2,64,4,14,4,15,4,16,4,17,119,91,203,4,18,4,19,127,
97,131,4,20,129,137,101,143,4,21,123,105,145,4,22,4,23,147,111,155,
113,115,4,24,149,151,4,25,153,4,26,4,27,163,4,28,165,209,125,
4,29,4,30,4,31,4,32,4,33,4,34,133,135,4,35,4,36,4,37,4,38,139,141,
4,39,4,40,4,41,4,42,4,43,4,44,4,45,4,46,4,47,4,48,4,49,4,50,4,51,4,52,
4,53,4,54,157,2,192,159,161,4,55,4,56,4,57,4,58,4,59,4,60,4,61,4,62,
175,169,181,171,197,2,256,2,320,2,384,2,1664,177,179,185,2,448,2,512,
183,191,2,576,189,187,2,640,2,704,2,768,2,832,2,896,193,195,2,960,2,1024,
2,1088,2,1152,199,201,2,1216,2,1280,2,1344,2,1408,205,207,2,1472,2,1536,
2,1600,2,1728,235,211,213,217,215,223,2,1792,221,219,229,2,1856,2,1920,
2,1984,2,2048,225,227,2,2112,2,2176,2,2240,2,2304,231,233,2,2368,2,2432,
2,2496,2,2560,237,1,239,1,241,1,241,2,-1,245,261,247,265,249,267,
273,251,271,253,255,2,12,281,257,303,259,307,2,0,2,1,2,4,2,3,2,2,
2,6,2,5,269,2,7,2,9,2,8,2,10,2,11,291,275,277,279,2,13,283,287,2,14,
2,15,299,313,285,383,2,16,289,309,2,17,357,423,293,295,321,297,317,
2,18,363,327,301,331,2,19,305,341,2,20,339,2,21,349,333,311,347,2,22,
315,351,2,23,361,379,319,389,2,24,323,371,325,367,2,25,397,395,329,
2,26,2,27,2,28,2,29,335,337,2,30,2,31,2,32,2,33,2,34,2,35,343,345,
2,36,2,37,2,38,2,39,2,40,2,41,2,42,2,43,353,355,2,44,2,45,2,46,2,47,
359,393,2,48,2,49,2,50,2,51,365,377,2,52,403,399,369,401,2,53,
373,4,64,375,409,2,54,407,405,2,55,381,387,2,56,415,385,391,2,57,2,58,
417,2,59,2,60,419,2,61,4,256,2,62,2,63,4,128,4,192,421,4,320,
4,384,4,448,4,512,4,576,4,640,4,704,4,768,4,832,4,896,4,960,411,413,
4,1024,4,1088,4,1152,4,1216,4,1280,4,1344,4,1408,4,1472,4,1536,4,1600,
4,1664,4,1728,449,425,427,431,429,437,4,1792,435,433,443,4,1856,4,1920,
4,1984,4,2048,439,441,4,2112,4,2176,4,2240,4,2304,445,447,4,2368,4,2432,
4,2496,4,2560,451,1,453,1,455,1,455,2,-1,
-1 } ;
/* Program options. Not changed except in main(). */
cap local = { DEFCAP } ; /* -c local capabilities */
char *argv0 = "?" ; /* program name (argv[0]) */
char *fnamepat= FNAMFT ; /* -r pattern for received file names */
char **fnames = 0 ; /* -t files to send */
char *getty = "" ; /* -g command for data calls */
char *iopt[ MAXIOPT ] ; /* -i modem init commands */
char *zopt[ MAXIOPT ] ; /* -z modem reset commands */
char *lkfiles [ MAXLKFILE+1 ] ; /* -x lock files */
char localid [ IDLEN + 1 ] ; /* -l local ID (telephone #) */
char *verb = "ewin" ; /* -v verbosity level */
int c1 = 0 ; /* -o1 use class 1 protocol */
int softadans = 0 ; /* -oa use software adaptive answer */
int igniniterr=0 ; /* -oe ignore initialization errors */
int niopt=0 ; /* number of init commands */
int nzopt=0 ; /* number of reset commands */
int cmdpause = T_CMD ; /* -oz delay before each init command */
int maxpgerr = 10 ; /* -q maximum errors per page */
int nfiles=0 ; /* number of files to send */
int share = 0 ; /* -s share fax device */
int waitforcall = 0 ; /* -w wait for incoming call */
u_char startchar = DC2 ; /* -ox character to start reception */
/* Bit ordering: serial devices transmit LS bit first. T.4/T.30 says MS
bit is sent first. `Normal' order therefore reverses bit order. */
u_char /* bit reversal lookup tables*/
reversebits [ 256 ] , normalbits [ 256 ] ,
*rxbitorder = normalbits , *txbitorder = normalbits ;
dtree *t4tree ; /* T.4 decoding tree */
/* fax stream variables (private). */
u_char faxibuf [ RCVBUFSIZE ] , /* fax input stream. */
*faxip=faxibuf , *faxiq=faxibuf ;
u_char faxobuf [ SNDBUFSIZE ] , /* fax output stream. */
*faxop=faxobuf , *faxoq=faxobuf+SNDBUFSIZE ;
int faxdev=-1 ; /* fax device file descriptor. */
/* session state variables. */
FILE *file=0 ; /* current image file */
cap remote ; /* remote capabilities */
cap session ; /* session capabilities */
char remoteid [ IDLEN + 1 ] ; /* ID (tel no) of remote station */
int crate = 19200 ; /* CONNECT rate */
int datamode=0 ; /* true if answered in data mode */
int decimate = 0 ; /* drop 1/2 lines to get 98 lpi */
int good=1 ; /* page received OK */
int hsc=-1 ; /* hangup status code (Class 2) */
int minlen = 0 ; /* pad lines to this many bytes */
/* Functions... */
/* Print time stamp. */
void tstamp( )
{
static time_t last = 0 , now ;
char *fmt = 0, tbuf [ MAXTSTAMP ] ;
now = time ( 0 ) ;
if ( now - last > 0 ) fmt = " (%M:%S)" ;
if ( now - last > 600 ) fmt = " %c" ;
if ( fmt ) {
strftime ( tbuf , MAXTSTAMP , fmt , localtime( &now ) ) ;
fputs ( tbuf , LOGF ) ;
last = now ;
}
}
/* For systems without strerror(3) */
char *strerror( int i )
{
extern int sys_nerr;
extern char *sys_errlist[];
return ( i >= 0 && i < sys_nerr ) ? sys_errlist[i] : "Unknown Error" ;
}
/* Return string corresponding to character c. */
char *cname ( u_char c )
{
#define CNAMEFMT "<0x%02x>"
#define CNAMELEN 6+1
static char *cname [ 256 ] = { /* character names */
"<NUL>","<SOH>","<STX>","<ETX>", "<EOT>","<ENQ>","<ACK>","<BEL>",
"<BS>", "<HT>", "<LF>", "<VT>", "<FF>", "<CR>", "<SO>", "<SI>",
"<DLE>","<XON>","<DC2>","<XOFF>","<DC4>","<NAK>","<SYN>","<ETB>",
"<CAN>","<EM>", "<SUB>","<ESC>", "<FS>", "<GS>", "<RS>", "<US>" } ;
static char names[ (127-32)*2 + 129*(CNAMELEN) ], *p=names ;
static int i=0 ;
if ( ! i )
for ( i=32 ; i<256 ; i++ ) {
cname [ i ] = p ;
sprintf ( p, i<127 ? "%c" : CNAMEFMT , i ) ;
p += strlen ( p ) + 1 ;
}
return cname [ c ] ;
}
/* Return name of frame of type 'fr'. */
const char *frname ( int fr )
{
static struct framenamestruct { int code ; const char *name ; }
framenames [] = {
{DIS,"DIS"},{CSI,"CSI"},{NSF,"NSF"},{CFR,"CFR"},{FTT,"FTT"},{MCF,"MCF"},
{RTN,"RTN"},{RTP,"RTP"},{PIN,"PIN"},{PIP,"PIP"},{DCS,"DCS"},{TSI,"TSI"},
{NSS,"NSS"},{CRP,"CRP"},{DCN,"DCN"},{EOM,"EOM"},{MPS,"MPS"},{EOP,"EOP"},
{PRI_EOM,"PRI-EOM"},{PRI_MPS,"PRI-MPS"},{PRI_EOP,"PRI-EOP"},
{DTC,"DTC"},{CIG,"CIG"},{NSC,"NSC"}, {0,0} }, *p ;
for ( p=framenames ; p->code && p->code != fr ; p++ ) ;
return p->code ? p->name : "UNKNOWN" ;
}
/* Print a message with a variable number of printf()-type arguments if the
first character appears in the verb[ose] string. Other leading
characters and digits do additional actions: + allows the message to be
continued on the same line, '-' buffers the message instead of printing
it, E, and W expand into strings, S prints the error message for the
most recent system error, 0-4 set the return value, a space is skipped
and ends prefix. Returns 0 if no prefix digit. */
int msg ( const char *fmt, ... )
{
int err=0, dolf=1, flush=1 ;
char *ps="", *pe="", *pw="" ;
const char *p ;
static int atcol1=1 ;
va_list ap ;
va_start ( ap, fmt ) ;
for ( p=fmt ; *p ; p++ ) {
switch ( *p ) {
case ' ': p++ ; goto print ;
case 'A': break ; /* program args */
case 'C': break ; /* commands/responses */
case 'M': break ; /* modem dialogue */
case 'N': break ; /* negotiation */
case 'E': pw = "Error: " ; break ;
case 'H': break ; /* HDLC frame data */
case 'I': break ; /* information */
case '+': dolf = 0 ; break ;
case '-': flush = 0 ; break ;
case 'R': break ; /* reception errors */
case 'S': ps = strerror ( errno ) ; break ;
case 'W': pw = "Warning: " ; break ;
case '0': case '1': case '2': case '3': case '4': case '5':
err = *p - '0' ; break ;
default: goto print ;
}
}
print:
if ( strchr ( verb , tolower ( *fmt ) ) ) {
if ( atcol1 ) fprintf ( LOGF , "%s: %s%s" , argv0 , pe , pw ) ;
vfprintf( LOGF, p , ap ) ;
fputs ( ps , LOGF ) ;
if ( ( atcol1 = dolf ) ) {
tstamp ( ) ;
fputs ( "\n" , LOGF ) ;
}
if ( flush ) fflush ( LOGF ) ;
}
va_end ( ap ) ;
return err ;
}
/* Unpack the decoding tree. If the LS bit is 1, the entry is a
pointer to the next node in a code word, else it is a terminal
node and the value is a pointer to where to continue scanning
for the next code word and the following value is the run
length (or -1 or -2). */
dtree *unpktree ( )
{
static dtree nodes [ T4NODES ], *p ;
const short int *l ;
for ( p=nodes, l=pkdt4tree ; *l > 0 ; p++ ) {
if ( p - nodes >= T4NODES ) {
fprintf ( stderr, "error in T.4 decoding tree\n" ) ; break ;
}
if ( *l & 1 ) { p->zero = nodes + *l++/2 ; }
else { p->znext = nodes + *l++/2 ; p->zlen = *l++ ; } ;
if ( *l & 1 ) { p->one = nodes + *l++/2 ; }
else { p->onext = nodes + *l++/2 ; p->olen = *l++ ; } ;
}
return nodes + 1 ; /* search starts at node 1 (white runs) */
}
/* millisecond delays (for systems without usleep). */
void msleep ( int t )
{
struct timeval timeout ;
timeout.tv_sec = t / 1000 ;
timeout.tv_usec = ( t % 1000 ) * 1000 ;
if ( select ( 1 , 0 , 0 , 0 , &timeout ) < 0 )
msg ("ES2select failed in msleep:") ;
}
/* Return number of characters ready to read or < 0 on error. t
is tenths of a second of idle time before timing out. If t is
negative, waits forever. */
int faxdata ( int t )
{
int n ;
fd_set fds ;
struct timeval timeout ;
if ( faxdev < 0 ) msg ("Ecan't happen (faxdata)") ;
timeout.tv_sec = t / 10 ;
timeout.tv_usec = ( t % 10 ) * 100000 ;
FD_ZERO ( &fds ) ;
FD_SET ( faxdev , &fds ) ;
n = select ( faxdev+1 , &fds , 0 , 0 , t<0 ? 0 : &timeout ) ;
if ( n < 0 ) msg("ES2select failed in faxdata:") ;
return n ;
}
/* faxundrflw is called only by the faxgetc() macro when the buffer is
empty. t is maximum idle time before giving up. Returns number of
characters read or EOF on timeout or errors. */
int faxundrflw ( int t )
{
int n = faxdata ( t ) ;
if ( n > 0 )
if ( ( n = read( faxdev , faxibuf , RCVBUFSIZE ) ) < 0 )
msg ("ES2fax device read:") ;
faxiq = ( faxip = faxibuf ) + ( n > 0 ? n : 0 ) ;
return n > 0 ? n : EOF ;
}
/* faxgetc is a macro like getc(). It returns the next character
from the fax device or EOF after idle time t. */
#define faxgetc(t) ( faxip >= faxiq && faxundrflw(t) == EOF ? EOF : \
*(u_char*)faxip++ )
/* A function like faxgetc that also removes DLE escapes, detects DLE-ETX
terminators and fixes bit order. Does not ignore invalid escape
sequences. Returns the character read, EOF on error/timeout, or -2 on
DLE-ETX. */
int faxgetdatac ( int t )
{
int c ;
return ( c = faxgetc(t) ) == DLE && ( c = faxgetc(t) ) == ETX ? -2 :
normalbits [ c & 0xff ] ;
}
/* Get a modem response into buffer s, storing up to n bytes. The response
begins with most recent non-control character and ends with CR/LF.
Returns s or null if times-out in t deciseconds or on i/o error. Trace
messages are buffered to reduce possible timing problems. */
char *faxgets( char *s , int n , int t )
{
int c=0, sta=0 ;
char *p = s ;
while ( sta < 4 ) {
if ( ( c = faxgetc( t ) ) == EOF ) break ;
c &= 0x7f ;
if ( ! sta ) msg ( "M-+ [" ) ;
msg ( "M-+ %s" , cname ( c ) ) ;
switch ( sta ) {
case 0 :
case 1 : sta = ( iscntrl ( c ) ? 1 : (p=s, 2) ) ; break ;
case 2 : sta = c == CR ? 3 : ( iscntrl ( c ) ? 1 : 2 ) ; break ;
case 3 : sta = c == LF ? 4 : ( iscntrl ( c ) ? 1 : (p=s, 2) ) ; break ;
default: msg ( "Ecan't happen (faxgets)" ) ;
}
if ( sta == 2 && p < s+n-1 ) *p++ = c ;
}
*p = 0 ;
if ( p >= s+n-1 ) msg ( "W- modem response overflow" ) ;
if ( sta ) msg ( "M- %s]" , c == EOF ? "<timeout>" : "" ) ;
return c == EOF ? 0 : s ;
}
/* Write the fax output buffer to fax output device. To allow zero-fill
underflow padding, should be called after writing the EOL zero
byte. Warns if called with full buffer (presumably called from
faxputc()). Returns 0 or EOF on error. */
int faxflush ( )
{
int n=0 ;
u_char *p = faxobuf ;
if ( faxdev < 0 ) msg ("Ecan't happen (faxflush)") ;
if ( faxop >= faxoq ) msg ("Wfax output buffer overflow") ;
while ( p < faxop && ( n = write( faxdev , p , faxop - p ) ) >= 0 )
p += n ? n : msg ( "W0wrote %d of %d bytes" , n , faxop - p ) ;
return n >= 0 ? (faxop = faxobuf , 0) : (msg ("ESfax device write:") , EOF) ;
}
/* faxputc() is a macro like putc(). It returns the character written or
EOF on error. */
#define faxputc(c) ( faxop >= faxoq && faxflush() == EOF ? EOF : \
(u_char) ( *faxop++ = (c) ) )
/* faxobytes() returns number of bytes in fax output buffer */
#define faxobytes() ( faxop - faxobuf )
/* A function like faxputc but also escapes DLEs and sets proper bit
order. */
int faxputdatac ( u_char c )
{
int x ;
x = normalbits [ c ] ;
if ( x == DLE ) x = faxputc ( x ) ;
return x < 0 ? x : faxputc ( x ) ;
}
/* Send character or string to modem immediately (for commands). Returns
like putc() and puts(). */
int faxputcnow ( char c )
{
return faxputc ( c ) < 0 ? EOF : ( faxflush() ? EOF : c ) ;
}
int faxputs ( const char *s )
{
int n=0 ;
while ( s && *s && ( n = faxputcnow ( *s++ ) ) != EOF ) ;
return n ;
}
/* Compare current termios state with termios struct t. Returns 0 if equal,
1 otherwise. */
int checktermio ( struct termios *t )
{
struct termios s ;
return tcgetattr ( faxdev , &s ) != 0 ||
t->c_iflag != s.c_iflag || t->c_oflag != s.c_oflag
|| t->c_lflag != s.c_lflag || t->c_cflag != s.c_cflag
|| t->c_cc[VMIN] != s.c_cc[VMIN] || t->c_cc[VTIME]!= s.c_cc[VTIME] ;
}
/* Set serial port mode. First time through saves initial settings. Sets
raw, 8-bit, 19.2 kbps mode with no flow control or as required. Returns
0 or 2 on error. */
int ttymode ( enum ttymodes mode )
{
int err=0 ;
static int saved=0 ;
static struct termios old, t ;
if ( faxdev < 0 ) msg ("Ecan't happen (ttymode/faxdev)") ;
tcdrain ( faxdev ) ;
tcflush ( faxdev , TCIOFLUSH ) ; /* make sure */
if ( ! saved ) {
if ( tcgetattr ( faxdev , &t ) ) {
err = msg ("ES2tcgetattr failed:") ;
} else {
old = t ;
saved = 1 ;
}
} else {
if ( checktermio ( &t ) ) msg ( "Wterminal mode corrupted" ) ;
}
t.c_iflag = IGNBRK | IGNPAR | IXANY ;
t.c_oflag = 0 ;
t.c_cflag = CS8 | CREAD | CLOCAL | HUPCL ;
t.c_lflag = 0 ;
t.c_cc[VMIN] = 1 ;
t.c_cc[VTIME] = 0 ;
cfsetospeed ( &t , B19200 ) ;
cfsetispeed ( &t , B19200 ) ;
if ( ! err )
switch ( mode ) {
case COMMAND : break ;
case DROPDTR : cfsetospeed ( &t , B0 ) ; break ;
case SEND : t.c_iflag |= IXON ; break ;
case ORIGINAL : if ( saved ) t = old ; break ;
default : err = msg ("E2can't happen(ttymode)") ; break ;
}
tcdrain ( faxdev ) ; /* make *sure* output done B4 turn off f/c */
tcflow ( faxdev , TCOON ) ; /* in case XON got lost */
if ( ! err && tcsetattr ( faxdev , TCSANOW , &t ) )
err = msg ( "ES2tcsetattr failed:" ) ;
if ( !err && checktermio ( &t ) )
err = msg ( mode == ORIGINAL ?
"W0terminal mode not restored properly" :
"E2terminal mode not set properly" ) ;
return err ;
}
/* Convert capability string to cap struct. Returns 0 or 2 on errors. */
int str2cap ( char *s, cap c )
{
int err=0, i=0, n ;
char *p ;
for ( n=0, p=s ; ! err && *p && i<NCAP ; p++ )
if ( isdigit ( *p ) ) c [ n++ ] = *p - '0' ;
else if ( isspace ( *p ) || *p == ',' ) ;
else err = msg ("E2invalid character (%c) in (%s)", *p , s ) ;
if ( n < NCAP ) msg ( "W0missing value(s) in capability \"%s\"", s ) ;
for ( i=0 ; i<n ; i++ )
if ( c [ i ] > capmax [ i ] || c [ i ] < 0 )
err = msg ( "E2%s = %d is %s", s , t30tab[i].name , c [ i ],
c[i]<0 ? "negative" : "too big" ) ;
return err ;
}
/* Print cap[ability] 'c' using text values and prefix 's'. */
void printcap ( char *s , cap c )
{
int i ;
msg ( "N-+ %s" , s ) ;
for ( i=0 ; i<NCAP ; i++ )
msg ( "N-+ %s" , c[i] > capmax [ i ] || c[i] < 0 ? "ERROR" :
capvaluestr [ i ] [ c[i] ] ) ;
msg ( "N-" , s ) ;
}
/* Convert a cap[ability] 'c' to a DIS/DCS/DTC FIF 'fif' of 'len' bytes.
Converts into DIS format if 'isdis' is true, else into DCS/DTC
format. Returns 0 or 3 on internal error (bad conversion tables). */
int mkdis ( cap c , u_char *fif , int len , int isdis )
{
int err=0, i, j, k ;
t30tabst *p ;
if ( len < 3 || len > 5 )
len = msg ( "W3bad DCS/DIS length (%d) set to 3" , len ) ;
fif[0] = 0 ;
fif[1] = isdis ? 0xc0 : 0x40 ;
for ( i=2 ; i<len-1 ; i++ ) fif[i] = 0x01 ; /* add extension bits */
fif[i] = 0 ;
for ( i=0 ; p=t30tab+i, i<NCAP ; i++ ) {
j = c [ i ] ;
if ( j > capmax [ i ] || j < 0 ) {
j = 0 ;
err = msg ( "E3mkdis: bad %s = %d set to 0", p->name, j ) ;
}
k = ( isdis ? p->captodis : p->captodcs ) [ j ] ;
if ( k == X ) {
k = 0 ;
err = msg ( "E3mkdis: can't happen (invalid %s)", p->name ) ;
}
if ( p->byte < len ) fif [ p->byte ] |= k << p->shift ;
}
return err ;
}
/* Return length of DIS/DTC FIF (counts extension bits). */
int dislen ( u_char *fif )
{
int n ;
for ( n=3 ; fif [ n-1 ] & 0x01 ; n++ ) ;
return n ;
}
/* Convert received DIS/DCS/DTC FIF to cap. Returns 0 or 3 on error (bad
DIS/DCS field). */
int mkcap ( u_char *fif, cap c , int dis )
{
int err=0, i, j, k, len ;
t30tabst *p ;
len = dislen ( fif ) ;
for ( i=0 ; i<NCAP ; i++ ) {
p=t30tab+i ;
if ( p->byte >= len ) {
c [ i ] = 0 ;
} else {
j = fif [ p->byte ] >> p->shift & p->mask ;
k = ( dis ? p->distocap : p->dcstocap ) [ j ] ;
if ( k == X ) {
c [ i ] = 0 ;
err = msg("E3mkcap: bad %s field (%d) set to 0", p->name, j) ;
} else {
c [ i ] = k ;
}
}
}
return err ;
}
/* Compute compatible local/remote capabilities. As side effect, sets
minimum line length and decimation. This routine is used by the sending
station only (or both in Class 2). Returns 0 if OK or 3 if no compatible
settings possible. */
int mincap ( cap local, cap remote, cap session )
{
int err=0, i ;
int msttab[2][8] = { { 0,1,3,3,5,5,7,7 } , { 0,1,1,3,3,5,5,7 } } ;
for ( i=0 ; i<NCAP && i!=ST ; i++ )
session[i] = remote[i] < local[i] ? remote[i] : local[i] ;
session[ST] = msttab [ session[VR] ] [ remote[ST] ] ;
minlen = (cps[session[BR]] * delay[session[ST]] + 500) / 1000 ;
decimate = local[VR] == 1 && session[VR] == 0 ;
printcap ( "local ", local ) ;
printcap ( "remote ", remote ) ;
printcap ( "session", session ) ;
msg ( "N- padding to %d bytes/line.%s", minlen,
decimate ? " reducing 198->96 lpi." : "" ) ;
if ( local[WD] != session[WD] || local[LN] != session[LN] ||
local[DF] != session[DF] )
err = msg ("E3can't convert image to remote capabilities" ) ;
return err ;
}
/* Search for a match to the string s in a null-terminated array of
possible prefix strings pointed to by p. The first character of each
prefix string is skipped. Returns pointer to the table entry or NULL if
not found. */
char *strtabmatch ( char **p, char *s )
{
while ( *p && strncmp ( *p+1 , s , strlen ( *p+1 ) ) ) p++ ;
return ( ! *p || **p == '-' ) ? NULL : *p ;
}
/* Send command to modem and check responses. Collects pending
(unexpected) responses and then pauses for inter-command delay
(cmdpause) if t is negative. Writes command s to modem if s is not
null. Reads responses and terminates when a response is one of the
prompts in responses[] or if times out in t deciseconds. Notes and
processes important class 2 responses (FPTS: Page Transfer Status, FHNG:
HaNGup status, and FDCS: Current Session capabilities). Repeats command
if detects a RING response (probable collision). Returns the first
character of the matching prefix string (e.g. 'O' for OK, 'C' for
CONNECT, etc.) or EOF if no such response was received within timeout
t. */
int cmd ( const char *s , int t )
{
char buf [ CMDBUFSIZE ] , *p = "" ;
int ppr ;
retry:
while ( s && faxgets ( buf , CMDBUFSIZE , t<0 ? cmdpause : 0 ) )
msg ( "W- unexpected response \"%s\"", buf ) ;
msg ( s ? "C- command \"%s\"" : "C- waiting" , s ) ;
if ( s ) { faxputs ( "AT" ) ; faxputs ( s ) ; faxputcnow ( CR ) ; }
while ( t && ( p = faxgets( buf , CMDBUFSIZE , t<0 ? -t : t ) ) ) {
msg ( "C- response \"%s\"" , p ) ;
if ( ! strncmp ( buf, "DATA" , 4 ) ||
! strncmp ( buf, "CONNECT DATA" , 12 ) )
datamode = 1 ;
if ( ! strncmp ( buf, "+FDCS:", 6 ) && ! str2cap ( buf+6 , session ) )
mincap ( local, session, session ) ; /* set decimation & minlen */
if ( sscanf ( buf , "+FPTS: %d", &ppr ) > 0 ) good = ppr & 1 ;
sscanf ( buf, "+FHNG: %d", &hsc ) ;
sscanf ( buf, "CONNECT %d", &crate ) ;
if ( ( p = strtabmatch ( (char**) prompts , buf ) ) ) break ;
if ( ! strncmp ( buf, "RING", 4 ) ) { msleep(100) ; goto retry ; }
}
return p ? *p : EOF ;
}
/* Send command to modem and wait for either of two possible replies after
testing (and possibly setting) current error status via err pointer. */
void ckcmd ( int *err, const char *s, int t, int r1, int r2 )
{
int c ;
if ( ! *err )
if ( ( c = cmd ( s, t ) ) != r1 && c != r2 )
if ( c == EOF )
*err = msg ("E3modem command (%s) timed out", s ? s : "none" ) ;
else
*err = msg ("E3wrong response to command (%s)", s ? s : "none" ) ;
}
/* Test for UUCP lock file & remove stale locks. Returns 0 on null file
name or if no longer locked, 1 if locked by another pid, 2 on error, 3
if locked by us. */
int ttlocked ( char *fname )
{
int err=0 ;
FILE *f ;
pid_t pid = 0 ;
char buf [ FILENAME_MAX ] = "" ;
if ( fname && *fname == HDBLKFLAG ) fname++ ;
if ( fname && ( f = fopen ( fname , "r" ) ) ) {
if ( fread ( buf, sizeof(char), FILENAME_MAX-1, f ) == sizeof(pid_t) ||
sscanf ( buf , "%d" , &pid ) != 1 ) pid = * (pid_t *) buf ;
if ( kill ( pid , 0 ) && errno == ESRCH )
if ( unlink ( fname ) ) err =
msg ( "ES2can't remove stale lock %s from pid %d:" , fname , pid ) ;
else err =
msg ( "I0removed stale lock %s from pid %d" , fname , pid ) ;
else
if ( pid != getpid() ) err = 1 ;
else err = 3 ;
fclose ( f ) ;
}
return err ;
}
/* Create UUCP (text or binary) lock file. Returns 0 on null
file name or if created, 1 if locked by another pid, 2 on
error, 3 if locked by us. */
int ttlock ( char *fname )
{
int err=0, dirlen, hdb=0 ;
FILE *f=0 ;
pid_t pid = getpid ( ) ;
char *p , buf [ FILENAME_MAX ] = "" ;
if ( fname && *fname == HDBLKFLAG ) { fname++ ; hdb=1 ; }
if ( fname && ! ( err = ttlocked ( fname ) ) ) {
dirlen = ( p = strrchr( fname , '/' ) ) ? p-fname+1 : strlen ( fname ) ;
sprintf ( buf , "%.*sTMP..%05d" , dirlen , fname , pid ) ;
if ( ( f = fopen( buf, "w" ) ) &&
( hdb ?
(fprintf(f, "%10d\n", pid)>0) : fwrite(&pid, sizeof(pid_t), 1, f) )
) {
if ( rename ( buf , fname ) == 0 ) chmod ( fname , 0444 ) ;
else if ( ! ( err = ttlocked ( fname ) ) )
err = msg ( "ES2can't rename lock file %s to %s:\n", buf, fname ) ;
} else {
err = msg ( "ES2can't open/write pre-lock file %s:\n", buf ) ;
}
}
if ( f ) { fclose ( f ) ; if ( err ) unlink ( buf ) ; }
return err ;
}
/* Remove lock file. Returns 0 on null file name, doesn't exist, or was
removed, 1 if the lock is to another pid, 2 on errors. */
int ttunlock ( char *fname )
{
int err = 0 ;
if ( fname && *fname == HDBLKFLAG ) { fname++ ; }
if ( fname && ( err = ttlocked ( fname ) ) ) {
if ( err == 1 ) msg ( "Ewon't remove lock %s (not ours)" , fname ) ;
if ( err == 3 )
if ( unlink ( fname ) ) err = msg ( "ES2can't remove lock %s:", fname ) ;
else err = 0 ;
}
return err ;
}
/* Lock all lock files. Returns 0 if all locks [already] applied, 1 if any
are locked to other pids, 2 on any errors. */
int lockall ( )
{
int err = 0 ;
char **p = lkfiles ;
while ( *p && ! err )
if ( ( err = ttlock ( *p++ ) ) == 3 ) err = 0 ;
return err ;
}
/* Remove all lock files. Returns 0 if all locks removed, 2 on errors. */
int unlockall ( )
{
int err = 0 ;
char **p = lkfiles ;
while ( *p ) if ( ttunlock ( *p++ ) ) err = 2 ;
return err ;
}
/* Resynchronize modem from an unknown state. If no immediate response,
try pulsing DTR low (needs &D{2,3,4}), and cancelling data or fax data
modes. In each case, discard any responses for about 2 seconds and then
try command s. Returns 0 if OK or 4 if no response. */
int faxsync( char *s )
{
int err=0, method=0 ;
while ( ! err ) {
switch ( method++ ) {
case 0 :
break ;
case 1 :
msg ("Isync: dropping DTR") ;
ttymode ( COMMAND ) ; msleep ( 200 ) ;
ttymode ( DROPDTR ) ; msleep ( 200 ) ;
ttymode ( COMMAND ) ;
break ;
case 2 :
msg ("Isync: sending escape") ;
faxputs ( DLE_ETX ) ;
msleep ( 1500 ) ;
faxputs ( "+++" ) ;
break ;
case 3 :
err = msg ("E4sync: modem not responding") ;
continue ;
}
while ( cmd ( 0 , method ? TO_RESET : 1 ) != EOF ) ;
if ( cmd ( s , -TO_RESET ) == OK ) break ;
}
return err ;
}
/* Terminate session. Makes sure modem is responding, sends modem reset
commands, resets fax device to original state, removes lock files. */
int end_session ( )
{
int i, err ;
err = faxsync ( "Q0V1" ) ;
for ( i=0 ; ! err && i<nzopt ; i++ )
if ( cmd( zopt[i] , -TO_RESET ) != OK && ! igniniterr )
err = msg ("E3modem reset command (%s) failed", zopt[i]) ;
if ( ! err ) err = ttymode ( ORIGINAL ) ;
unlockall ( ) ;
return err ;
}
/* signal handler: hang up and exit */
void onsig ( int sig )
{
msg ( "Eterminating on signal %d", sig ) ;
end_session ( ) ;
msg ("Idone, returning 5") ;
exit(5) ;
}
/* Initialize session. Try locking and opening fax device until opened or
get error. Then set tty modes, register signal handler, setup
modem. Returns 0 if OK, 2 on errors, 3 if initialization failed, 4 if no
modem response. */
int begin_session ( char *faxfile )
{
int i , err=0 , busy=0 , flags;
do {
err = lockall ( ) ;
if ( ! err ) {
faxdev = open ( faxfile , O_RDWR | O_NDELAY ) ;
if ( faxdev < 0 )
if ( errno == EBUSY ) err = 1 ;
else err = msg ( "ES2fax device open\n%s:", faxfile ) ;
if ( ! err )
if ( ( flags = fcntl( faxdev, F_GETFL, 0 ) ) < 0 ||
fcntl( faxdev, F_SETFL, ( flags & ~O_NDELAY ) ) < 0 )
err = msg ( "ES2fax device fcntl\n%s:", faxfile ) ;
}
if ( err == 1 ) {
if ( busy++ < 1 )
msg ( "Wfax device %s locked or busy. waiting...", faxfile ) ;
sleep ( LOCKPOLLDELAY ) ;
}
} while ( err == 1 ) ;
if ( ! err ) msg ( "Iopened %s on fd %d", faxfile, faxdev ) ;
if ( ! err ) err = ttymode ( COMMAND ) ;
for ( i=0 ; ! err && catch [ i ] ; i++ )
if ( signal ( catch [ i ] , onsig ) == SIG_ERR )
err = msg ( "ES2can't set signal %d handler:", catch [ i ] ) ;
if ( !err ) err = faxsync("Q0V1") ;
for ( i=0 ; ! err && i<niopt ; i++ )
if ( cmd( iopt[i] , -TO_RESET ) != OK && ! igniniterr )
err = msg ("E3modem initialization command (%s) failed", iopt[i]) ;
return err ;
}
/* Position `file' to start of page p (1...nfiles). Assumes global
variables 'fnames' and 'nfiles,' have been initialized. This function
looks ahead to see if there are more pages and if they are of a
different format. Eventually will handle multi-page files with embedded
format information (e.g. TIFF-F). Sets ppm to MPS if more pages to be
sent with same format, EOM if more pages with different format, EOP if
no more pages. Returns 0 if OK, 2 on errors. Currently sets ppm to MPS
or EOP only. */
int rdpage ( int p , int *ppm )
{
int err=0 ;
if ( file && fclose ( file ) ) msg ("ES2file close:") ;
if ( p <= nfiles && p > 0 ) {
if ( ( file = fopen ( fnames[p-1] , "rb" ) ) ) {
msg ( "Iopened \"%s\" (p %d/%d)", fnames[p-1] , p, nfiles ) ;
*ppm = p >= nfiles ? EOP : MPS ;
} else {
err = msg ("ES2 file open\n%s:", fnames[p-1]) ;
}
} else {
err = msg ("E2 bad page number (%d)", p ) ;
}
return err ;
}
/* Same as rdpage() but creates new file name and opens `file' for
writing. If page is -1 removes the most recently opened file. Assumes
'fnampat' contains the strftime(3) pattern to be used for generating
file names. Returns 0 if OK, 2 on errors. */
int wrpage ( int p )
{
int err=0 ;
static char fname [ FILENAME_MAX + 1 ] , base [ FILENAME_MAX + 1 ] ;
time_t t ;
t = time(0) ;
if ( ! *base ) strftime( base, FILENAME_MAX, fnamepat, localtime ( &t ) ) ;
if ( file && fclose ( file ) ) msg ("ES2file close:") ;
if ( p == -1 ) {
if ( remove ( fname ) )
err = msg ( "ES2deleting file %s", fname ) ;
else
msg ( "Iremoved %s", fname ) ;
} else {
if ( *fname ) msg ( "Ireceived -> %s", fname ) ;
sprintf ( fname, "%.*s.%03d", FILENAME_MAX - 5, base, p ) ;
if( ( file = fopen ( fname , "rb" ) ) )
err = msg ( "E2file (%s) already exists." ) ;
else
if ( ( file = fopen ( fname , "wb" ) ) == 0 )
err = msg ("ES2opening file\n%s:", fname) ;
if ( ! err ) msg ( "Iopened file %s", fname ) ;
}
return err ;
}
/* Send data for one page. Enable serial port flow control. Read
characters from file and pad lines if necessary. Bit-reverse characters
and escape DLE before storing in buffer. Flush buffer if enough
characters stored. Append RTC if needed. Send DLE-ETX and return
serial port to command mode when done. Returns 0 if OK, non-0 on
errors. */
int send_data( )
{
int done=0, err=0;
int lastc=0, c ;
int eol=0, len=0, llen=0, eolcnt=0, rtc=0, skip=0 ;
int bytes=0, pad=0, linec=0, lines=0 ;
int i, noise=0 ;
time_t start, dt ;
dtree *t4p = t4tree ;
done = err = ttymode ( SEND ) ;
start = time(0) ;
while ( ! done ) {
if ( ( c = getc ( file ) ) != EOF ) {
#define T4CODE(l) ( l < 0 ? (eol=l,llen=len,len=0) : (len+=l) )
T4CTST(c) ;
#undef T4CODE
if ( eol ) {
if ( ! llen ) {
if ( ++eolcnt >= 6 ) done = rtc = 1 ;
} else {
eolcnt=0 ;
}
if ( eol == -2 )
msg ("W- image error (line %d)", lines ) ;
if ( ! skip ) {
lines++ ;
while ( ( linec < minlen || lastc ) ) {
faxputc ( lastc = 0 ) ;
linec++ ;
pad++ ;
}
linec = 0 ;
}
if ( decimate ) {
if ( skip ) {
skip = 0 ;
} else {
faxputc ( lastc = 0 ) ; /* force enough zeroes for next EOL */
pad++ ;
skip = 1 ;
}
}
if ( faxobytes() > MINWRITE ) {
if ( faxflush() ) done=err=2 ;
}
eol=0 ;
}
if ( ! skip || ! llen ) {
faxputc ( lastc = txbitorder [ c ] ) ;
linec ++ ;
bytes++ ;
if ( lastc == DLE ) { faxputc ( lastc = DLE ) ; linec++ ; pad++ ; }
}
} else {
done = 1 ;
if ( ferror ( file ) ) err = msg ("ES2file read:") ;
}
}
if ( ! err && ! rtc ) {
for ( i=0 ; i<T4RTCLEN ; i++ ) faxputdatac ( (T4RTC)[i] ) ;
msg ( "I- added RTC (%d bytes)" , T4RTCLEN ) ;
pad += T4RTCLEN ;
}
for ( ; ( c = faxgetc ( 0 ) ) != EOF ; noise++ )
msg ( "W-+%s" , cname ( c ) ) ;
if ( noise ) msg ("W- : %d byte(s) received while sending", noise ) ;
faxputs ( DLE_ETX ) ; /* and faxflush() */
ckcmd ( &err, 0, TO_DRAIN, OK, 0 ) ;
dt = time(0) - start ;
msg ("Isent %d lines %d+%d bytes %d s %d bps" ,
lines, bytes, pad, (int) dt, ((bytes+pad)*8)/dt ) ;
if ( ! err ) err = ttymode ( COMMAND ) ;
return err ;
}
/* Receive data. Get characters from the modem and check for
errors/EOF. Remove long runs of zeroes (T.4 FILL). If in DLE
escape check for DLE, end of data or protocol violation.
Otherwise, test for DLE & write the character out. Check that
the output file is still OK. If not, send one CANcel
character and wait for protocol to complete. */
int receive_data ( )
{
int done=0, err=0, nulls=0, c ;
int bytes=0, lines=0, nerr=0 ;
int eol=0, eolcnt=0, len=0, llen=0, lastlen=0, rtc=0 ;
dtree *t4p = t4tree ;
while ( ! done ) {
if ( ( c = faxgetc( TO_CHAR ) ) == DLE ) {
if ( ( c = faxgetc( TO_CHAR ) ) == ETX ) done = 1 ;
else if ( c != DLE ) {
msg ( "W- \"%s\" received after DLE", cname ( c ) ) ;
continue ;
}
}
if ( c == EOF ) {
done = err = msg ("E3fax device read timeout") ;
continue ;
}
if ( c == 0 ) {
if ( ++nulls > MAXNULLS ) {
continue ;
}
} else {
nulls = 0 ;
}
if ( ! done && ! rtc ) {
putc ( c = rxbitorder [ c ] , file ) ;
bytes++ ;
}
#define T4CODE(l) ( l<0 ? (eol=l, llen=len, len=0) : (len+=l) )
T4CTST(c) ;
#undef T4CODE
if ( eol ) {
lines++ ;
if ( ( ( llen != lastlen ) && llen && lastlen && lines > 3 ) ) {
nerr++ ;
msg ("R-+ (%d:%d)", lines, llen ) ;
}
lastlen = llen ;
if ( ! llen ) {
if ( ++eolcnt >= 6 ) rtc = 1 ;
} else {
eolcnt=0 ;
}
eol=0 ;
}
if ( ! err && ferror ( file ) ) {
err = msg ("ES2fax file write:") ;
faxputcnow ( CAN ) ;
msg ("Wreceive CANcelled") ;
}
} /* while */
if ( nerr ) msg ("R- : reception errors" ) ;
nerr /= 2 ;
if ( nerr ) msg ("W- %d reception errors", nerr ) ;
ckcmd ( &err, 0, TO_C2EOR, ( c1 ? NO : OK ) , 0 ) ;
if ( ! err && ! rtc ) {
fwrite ( T4RTC , sizeof ( u_char ) , T4RTCLEN , file ) ;
msg ( "I- added RTC (%d bytes)" , T4RTCLEN ) ;
}
msg ( "I- received %d lines, %d bytes, %d errors", lines, bytes, nerr ) ;
good = nerr < maxpgerr ;
return err ;
}
/* Send training check sequence of n zeroes. Returns 0 or 2 on error. */
int puttrain ( const char *s , int n )
{
int i, err=0 ;
ckcmd ( &err, s , TO_FT , CONNECT, 0 ) ;
if ( ! err ) {
ttymode ( SEND ) ;
for ( i=0 ; ! err && i < n ; i++ ) {
faxputc ( 0 ) ;
if ( ! ( i & 63 ) ) faxflush ( ) ;
}
faxputc ( 1 ) ; /* disable zero-fill padding */
faxputs ( DLE_ETX ) ;
ttymode ( COMMAND ) ;
msg ( "I- sent TCF (%d bytes)", n ) ;
ckcmd ( &err, 0, TO_DRAIN, OK, 0 ) ;
}
return err ;
}
/* Checks n bytes of received training check sequence. Skips first n/16
bytes. Returns 0 if OK or ignoring training errors, 1 if too many
errors/not enough data, or 3 on other errors. */
int gettrain ( const char *s , int n )
{
int err=0, c, i, errcnt=0, skip=n/16 ;
ckcmd ( &err, s , TO_FT , CONNECT, 0 ) ;
for ( i=0 ; ! err && ( c = faxgetdatac ( TO_CHAR ) ) >= 0 ; i++ )
if ( c != 0 && i >= skip && i < n+skip ) errcnt++ ;
ckcmd ( &err, 0, TO_RTCMD, NO, 0 ) ;
if ( !err )
msg ( "I- received TCF (%s: %d error(s) in %d/%d bytes)",
( err = i<n || errcnt>MAXTRAINERR ) ? "failed" : "passed" ,
errcnt, n, i ) ;
return err ;
}
/* Log sent/received HDLC frame. Display of these messages is delayed to
avoid possible timing problems. */
void logfr ( const char *s , const char *nm , u_char *p , int n )
{
int i=0 ;
msg ( "I- %s %s", s, nm ) ;
msg ( n > 10 ? "H- %s %d bytes:" : "H-+ %s %d bytes:" , s, n ) ;
for ( i=0 ; i<n ; i++ ) {
msg ( "H-+ %02x" , p[i] & 0xff ) ;
if ( ( i&0xf ) == 0xf && i != n-1 ) msg ( "H-" ) ;
}
msg ( "H-") ;
}
/* Send HDLC control frame of type type. Extra bits are OR'ed to the frame
type (FCF) if this frame follows a previous one (no +FTH required) or if
more frames will follow. Sets up flag, address, and fax control field
(FCF) bytes in `buf'. Sends these plus `len` additional bytes.
Terminates with DLE-ETX and checks response. Returns 0 if OK, 2 or 3 on
error. */
#define MORE_FR 0x100
#define SUB_FR 0x200
int putframe ( int type, u_char *buf , int len )
{
int err=0, i ;
buf [ 0 ] = 0xff ;
buf [ 1 ] = type & MORE_FR ? 0xc0 : 0xc8 ;
buf [ 2 ] = type & 0xff ;
if ( ! ( type & SUB_FR ) )
ckcmd ( &err, "+FTH=3" , TO_FT, CONNECT, 0 ) ;
if ( ! err ) {
ttymode ( SEND ) ;
for ( i=0 ; ! err && i< len+3 ; i++ ) faxputdatac ( buf [ i ] ) ;
faxputs ( DLE_ETX ) ;
ttymode ( COMMAND ) ;
ckcmd ( &err, 0, TO_DRAIN, type & MORE_FR ? CONNECT : OK , 0 ) ;
if ( ! err )
logfr ( "sent", frname(type & 0x7f), buf, len+3 ) ;
}
return err ;
}
/* Read HDLC frame and store it in buffer buf of size n. Skips
issuing +FRH command on pass==0. Returns length of frame if
OK, EOF on timeout, -3 if any errors as per T.30 5.4.2 (too
long, FCS error) */
int getframe ( int pass , u_char *buf , int n , int t )
{
int err=0, c, i=0 ;
if ( pass && ( c = cmd ( "+FRH=3" , t ) ) != CONNECT )
err = ( c == EOF ) ? -EOF : msg ( "E3get frame command failed") ;
if ( err == -EOF ) { faxputc ( CAN ) ; cmd ( "" , TO_ABRT ) ; }
if ( ! err ) {
for ( i=0 ; ( c = faxgetdatac ( pass ? TO_CHAR : t ) ) >= 0 ; i++ )
if ( i < n ) buf[ i ] = c ;
if ( c == EOF )
err = i ? msg ( "E3timed out reading frame data") : EOF ;
if ( i >= n ) err = msg ( "E3frame too long (%d bytes)", i ) ;
}
ckcmd ( &err, 0, TO_RTCMD, OK, CONNECT ) ;
return err ? -err : ( i < n ? i : n ) ;
}
/* Reverse bit and byte order of ID strings as per T.30 5.3.6.2.4-6 */
void revcpy ( u_char *from , u_char *to )
{
int i, j ;
for ( i=0, j=IDLEN-1 ; i<IDLEN ; i++, j-- )
to [ i ] = normalbits [ from [ j ] & 0xff ] ;
}
/* Handle procedure interrupt requests (just print message for now).
Returns 0. */
int pr_int( )
{
return msg ("W0procedure interrupt request ignored" ) ;
}
/* Class 1 send. Each received frame elicits an appropriate reply.
Optional or unrecognized frames are ignored. Terminates by sending DCN
after receiving MCF after EOP or on error. Timeouts, bad frames or CRP
repeat last command up to MAXRETRY times. On training check failures
the speed (remote capability br) is reduced. The lowest speed is retried
MAXTRAIN times. Page transmission failures are retried NTXRETRY
times. */
enum replies
{ NONE=0x100, DCS1, DCS2, TXDATA, PPM, DONE, BADFR, TIMEOUT, SENDDIS } ;
int c1send ( int started )
{
int err=0, done=0, pass=started, page=1, frame=NONE, reply=DONE ;
int frlen, rxdislen=0, disbit=0 ;
int cmdtry=0, pagetry=0, traintry=0 ;
int ppm=EOP ;
u_char buf [ MAXFRLEN ] , *fif=buf+3 ;
for ( pass=started ; ! done ; pass++ ) {
if ( err ) {
frame = NONE ;
} else {
frlen = getframe ( pass, buf, MAXFRLEN, started ? T3S : T1 ) ;
if ( frlen < 3 ) {
frame = CRP ;
} else {
frame = buf [ 2 ] & 0x7f ;
logfr ( "received" , frname(frame), buf , frlen ) ;
cmdtry = 0 ;
}
}
switch ( frame ) {
case CRP:
if ( !started || cmdtry++ >= MAXRETRY )
err = msg ( "E3 no response from remote" ) ;
break ;
case NSF:
reply = NONE ;
break ;
case CSI:
revcpy ( fif , (u_char*) remoteid ) ;
msg ( "I- remote ID=\"%*.*s\"" , IDLEN, IDLEN, remoteid ) ;
reply = NONE ;
break ;
case DIS:
started = 1 ;
disbit = 0x80 ;
rxdislen = dislen ( fif ) ;
mkcap ( fif , remote , 1 ) ;
reply = DCS1 ;
break ;
case CFR:
reply = TXDATA ;
break ;
case FTT:
if ( (remote[BR] = fallback[session[BR]]) > 0 || traintry++ < MAXTRAIN )
reply = DCS2 ;
else
err = msg ( "E1failed to train") ;
break ;
case PIP:
pr_int ( ) ;
case RTP:
case MCF:
page++ ;
pagetry=0 ;
if ( ppm == MPS && frame == MCF ) reply = TXDATA ;
else if ( ppm == EOP ) done = 1 ;
else reply = DCS2 ;
break ;
case PIN:
pr_int ( ) ;
case RTN:
if ( pagetry++ < NTXRETRY ) reply = DCS2 ;
else err = msg( "E1too many page send retries" ) ;
break ;
case DCN:
err = msg ( "E3remote disconnected") ;
break ;
case NONE:
break ;
default:
break ;
} /* switch ( frame ) */
switch ( err || done ? DONE : reply ) {
case DCS1:
revcpy ( (u_char*) localid , fif ) ;
err = putframe ( TSI + MORE_FR + disbit , buf, IDLEN ) ;
/* fall through */
case DCS2:
mincap ( local, remote, session ) ;
mkdis ( session , fif , rxdislen , 0 ) ;
if ( !err ) err =
putframe ( DCS + (reply==DCS1 ? SUB_FR : 0) + disbit, buf, rxdislen ) ;
ckcmd ( &err, "+FTS=8", TO_FT, OK, 0 ) ;
if ( !err ) err =
puttrain ( c1cmd [1][1][session[BR]] , 1.65 * cps [ session[BR] ] ) ;
reply = DCS2 ;
break ;
case TXDATA:
if ( !err ) err = rdpage ( page, &ppm ) ;
ckcmd ( &err, c1cmd [1][0][session[BR]] , TO_FT, CONNECT, 0 ) ;
if ( !err ) err = send_data ( ) ;
ckcmd ( &err, "+FTS=8", TO_FT, OK, 0 ) ;
/* fall through */
case PPM:
if ( !err ) err = putframe ( ppm + disbit, buf, 0 ) ;
reply = PPM ;
break ;
case DONE:
putframe ( DCN + disbit, buf, 0 ) ;
done = 1 ;
break ;
case NONE:
break ;
default:
err = msg ( "E3can't happen(reply)" ) ;
break ;
} /* switch ( reply ) */
} /* for ( ! done ) */
return err ;
}
/* Class 1 receive. Sends DIS until gets a DCS or times out. Sends ppr
after ppm and receives data after DCS or MPS. Note: TCF (training check
data) is received 75 +/- 20 ms after DCS so there should be no lengthy
processing between DCS and gettrain(). */
int c1receive ( int started )
{
int err=0, done=0, page=1, pass, frame, frlen ;
u_char buf[MAXFRLEN], *fif=buf+3 ;
err = wrpage ( page ) ;
for ( pass=started ; ! err && ! done ; pass++ ) {
if ( pass > 0 ) {
frlen = getframe(pass, buf, MAXFRLEN, started ? T2 : T4 ) ;
if ( frlen == EOF ) {
frame = started ? TIMEOUT : ( pass < MAXDIS ? SENDDIS : TIMEOUT ) ;
} else if ( frlen < 3 ) {
frame = started ? NONE : ( pass < MAXDIS ? SENDDIS : NONE ) ;
} else {
frame = buf [ 2 ] & 0x7f ;
logfr ( "received" , frname(frame), buf , frlen ) ;
}
} else {
frame=SENDDIS ;
}
switch ( frame ) {
case SENDDIS:
revcpy ( (u_char*) localid, fif ) ;
if ( !err ) err =
putframe ( CSI + MORE_FR + ( pass ? 0 : SUB_FR ), buf, IDLEN ) ;
mkdis ( local , fif , DEFDISLEN , 1 ) ;
if ( !err ) err =
putframe ( DIS + SUB_FR , buf, DEFDISLEN ) ;
break ;
case TIMEOUT:
done = err = msg ( "E3timed out waiting for sender" ) ;
break;
case BADFR :
msg ( "W bad frame" ) ;
break ;
case TSI: ;
revcpy ( fif , (u_char*) remoteid ) ;
msg ( "I- remote ID=\"%*.*s\"" , IDLEN, IDLEN, remoteid ) ;
break ;
case DCS: ;
started = 1 ;
mkcap ( fif , session , 0 ) ;
printcap ( "session" , session ) ;
if ( gettrain ( c1cmd [0][1][session[BR]], cps[session[BR]] ) == 0 ) {
if ( !err ) err = putframe ( CFR, buf, 0 ) ;
if ( !err ) goto getdata ;
} else {
if ( !err ) err = putframe ( FTT, buf, 0 ) ;
}
break ;
case PRI_EOM:
case PRI_MPS:
case PRI_EOP:
pr_int() ;
case EOM:
case MPS:
case EOP:
err = wrpage ( ++page ) ;
if ( ! err )
if ( good ) err = putframe ( MCF, buf, 0 ) ;
else err = putframe ( RTN, buf, 0 ) ;
if ( ! err && good && ( frame == MPS || frame == PRI_MPS ) )
goto getdata ;
if ( ! err && good && ( frame == EOM || frame == PRI_EOM ) ) {
pass=0 ;
ckcmd ( &err, "+FTS=8", TO_FT, OK, 0 ) ;
}
break ;
getdata:
ckcmd ( &err, c1cmd [0][0][session[BR]] , TO_FT, CONNECT, 0 ) ;
if ( !err ) err = receive_data ( ) ;
break ;
case DCN:
done = 1 ;
break;
} /* switch */
} /* do */
wrpage ( -1 ) ; /* remove last file */
return err ;
}
/* Class 2 fax transmission. Retries each page up to NTXRETRY times.
Transmission begins after DC2 or XON is received. Sends the data and
sends appropriate post-page message. Checks for 'good' status set in
cmd() by +FPTS: responses. */
int c2send ( )
{
int c, page, err=0, done=0, try, ppm=0, noise=0 ;
for ( page=1 ; !err && ! done ; page++ ) {
for ( try=good=0 ; !err && !good && try<NTXRETRY ; try++ ) {
err = rdpage ( page , &ppm ) ;
ckcmd ( &err, "+FDT" , TO_C2B , CONNECT, 0 ) ;
if ( ! err )
while ( !err && ( c = faxgetc ( TO_C2X ) ) != XON && c != DC2 )
if ( c == EOF ) {
msg ( "Wno XON/DC2 received after CONNECT") ;
break ;
} else {
msg ( "W+%s", cname ( c ) ) ;
noise++ ;
}
if ( noise ) {
msg ( "Wreceived (%d) characters while waiting to send", noise ) ;
noise = 0 ;
}
if ( ! err ) err = send_data ( ) ;
good = 1 ; /* assume good if no +FPTS: */
ckcmd ( &err, ppm == EOP ? "+FET=2" : "+FET=0" , TO_C2PP, OK, 0 ) ;
if ( hsc > 0 ) err = msg ( "E2abnormal termination (code %d)", hsc ) ;
}
if ( ppm == EOP ) done = 1 ;
if ( try >= NTXRETRY ) {
err = msg ( "E2too many page send retries" ) ;
cmd ( "+FK", T3S ) ;
}
}
return err ;
}
/* Class 2 fax reception. Send fax data receive command. If
response is OK, then no more pages are coming. If it's
CONNECT receive the data for one page. Returns 0 or >= 2 for
errors. */
int c2receive ( )
{
int err=0, page=1, done=0, c ;
err = wrpage ( page ) ;
while ( ! err && ! done && hsc < 0 ) {
if ( ( c = cmd ( "+FDR" , TO_C2R ) ) == CONNECT ) {
faxputcnow ( startchar ) ;
err = receive_data ( ) ;
ckcmd ( &err, good ? "+FPTS=1" : "+FPTS=2", T3S , OK, 0 ) ;
if ( ! err ) err = wrpage ( ++page ) ;
} else {
if ( c == OK ) { wrpage ( -1 ) ; done = 1 ; }
else err = msg ( "E3receive (+FDR) command failed") ;
}
}
if ( hsc > 0 ) err = msg ( "E2abnormal call termination (code %d)", hsc ) ;
return err ;
}
/* Dial a number and send a fax. */
int send ( char *tel , char *faxfile )
{
char c, s [ 128 ] ;
int err=0 ;
msg ( "Idialing %.127s", tel ) ;
sprintf ( s , "D%.127s" , tel ) ;
err = begin_session ( faxfile ) ;
if ( ! err ) {
if ( ( ( c = cmd ( s , TO_A ) ) == ( c1 ? CONNECT : OK ) ) && hsc < 0 )
msg ( "Iconnected" ) ;
else if ( c == BUSY )
err = msg ( "W1number is busy" ) ;
else
err = msg ( "E2can't establish session" ) ;
if ( ! err )
err = c1 ? c1send ( 0 ) : c2send ( ) ;
if ( err != 1 && err < 4 ) end_session( ) ;
}
return err ;
}
/* Receive a fax. Open modem device and initialize it. Remove locks if
sharing device with outgoing calls. If waiting for call, wait for modem
activity, else answer phone. Figure out what mode we answered in and
handle call appropriately. Re-lock if necessary. Then exec *getty or
run class 1 or class 2 reception routines. Modems prompt as follows
after answering: Class 0: CONNECT nnn for data; Class 1: FAX + CONNECT
for fax, DATA + CONNECT nnn for data, just CONNECT for fax if +FAE=0;
Class 2: CONNECT (data) or OK (fax). ("+FCON" and "CONNECT FAX" are
status messages, not prompts). */
int receive ( char *faxfile )
{
enum connectmode { NONE, DATAMODE, FAXMODE } ;
enum connectmode mode=NONE ;
int c=0, err ;
if ( ( err = begin_session ( faxfile ) ) == 0 ) {
if ( ! err && share ) err = unlockall ( ) ;
if ( ! err && waitforcall ) {
msg ( "Iwaiting for activity on %s", faxfile ) ;
faxdata ( -1 ) ;
msg ( "Iactivity detected at ") ;
}
if ( ! err && share ) {
msleep ( 200 ) ; /* let other programs lock port */
err = lockall ( ) ;
}
if ( ! err && softadans && *getty ) {
if ( cmd ( ( waitforcall ? 0 : "A" ) , TO_DATAF ) == CONNECT )
mode = DATAMODE ;
else {
int i ; /* abort data answer mode & set fax mode to try again */
for ( i=0 ; i<3 ; i++ )
if ( cmd ( c1 ? "+FCLASS=1" : "+FCLASS=2" , -TO_RESET ) == OK )
break ;
}
}
if ( ! err && mode == NONE ) {
c = cmd ( ! waitforcall || ( softadans && *getty ) ? "A" : 0 , TO_A ) ;
if ( c1 )
mode = ( c == CONNECT ) ? ( datamode ? DATAMODE : FAXMODE ) : NONE ;
else
mode = ( c == CONNECT ) ? DATAMODE : ( c == OK ? FAXMODE : NONE ) ;
}
if ( err || hsc >= 0 ) mode = NONE ;
if ( ! err )
switch ( mode ) {
case DATAMODE : {
char buf [ MAXGETTY ] ;
msg ( "Idata call answered") ;
sprintf ( buf , getty , crate, crate, crate, crate, crate, crate ) ;
msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
err = msg ( "ES2exec failed:" ) ;
break ; }
case FAXMODE :
msg ( "Ifax call answered") ;
break ;
case NONE:
err = msg ( "E3unable to answer call") ;
break ;
}
if ( ! err ) err = c1 ? c1receive ( 0 ) : c2receive ( ) ;
if ( err != 1 && err < 4 ) end_session( ) ; /* if not locked */
}
return err ;
}
/* Simple (one option per argument) version of getopt(3). */
int optind = 1 ;
char *optarg ;
int nextopt( int argc, char **argv, char *args )
{
char *a, *p ;
if ( optind >= argc || *(a = argv[optind]) != '-' ) return -1 ;
optind++ ;
if ( ! *(a+1) || ! ( p = strchr ( args , *(a+1) ) ) )
return msg ( "Eunknown option (%s)" , a ) , '?' ;
if ( *(p+1) != ':' ) optarg = 0 ;
else
if ( *(a+2) ) optarg = a+2 ;
else
if ( optind >= argc ) return msg ( "Eno argument for (%s)", a ) , '?' ;
else optarg = argv [ optind++ ] ;
return *(a+1) ;
}
/* Fax send/receive program for Class 1 or 2 fax modems. Initialize
character name, bit reversal and T.4 decoding tables and process
arguments. Returns 0 on success, 1 if number busy or device locked, 2
for errors, 3 for protocol errors, 4 if no modem response, 5 on fatal
signal. */
int main( int argc, char **argv)
{
int i, err=0 , c, nlocks=0 ;
char *faxfile = FAXFILE ;
char capinit [ CMDBUFSIZE ] , idinit [ CMDBUFSIZE ] ;
char msgbuf [ MAXMSGBUF ] ;
argv0 = argv[0] ;
setvbuf ( LOGF , msgbuf , _IOFBF , MAXMSGBUF ) ;
msg ( "I " Version " starts ") ;
msg ( "I " Copyright " (compiled "__DATE__ " " __TIME__ ")" ) ;
argv0 = strrchr ( argv0 , '/' ) ;
if ( ! argv0 ) argv0 = argv[0] ; else argv0++ ;
cname ( 0 ) ;
for ( i=0 ; i<256 ; i++ ) normalbits [ reversebits [ i ] = i ] =
( i& 1 ? 128:0 ) | ( i& 2 ? 64:0 ) | ( i& 4 ? 32:0 ) | ( i& 8 ? 16:0 ) |
( i&16 ? 8:0 ) | ( i&32 ? 4:0 ) | ( i&64 ? 2:0 ) | ( i&128 ? 1:0 ) ;
t4tree = unpktree( ) ;
while (!err && (c=nextopt(argc,argv,"c:d:g:i:l:o:q:r:st:v:wx:z:T") ) != -1) {
switch (c) {
case 'c':
err = str2cap ( optarg , local ) ;
sprintf ( capinit , "+FDCC=%.*s" , CMDBUFSIZE-7, optarg ) ;
if ( !err && !c1 ) { optarg = capinit ; goto addopt ; }
break ;
case 'l':
if ( strlen ( optarg ) > IDLEN )
msg("Wlocal ID (%s) truncated to 20 characters", optarg ) ;
if ( strspn ( optarg, " +0123456789" ) != strlen ( optarg ) )
msg("Wlocal ID (%s) has non-standard characters", optarg ) ;
sprintf ( localid, "%*.*s", IDLEN, IDLEN, optarg ) ;
sprintf ( idinit , "+FLID=\"%.*s\"" , CMDBUFSIZE-9, localid ) ;
if ( !c1 ) { optarg = idinit ; goto addopt ; }
break ;
case 'i':
addopt:
if ( niopt < MAXIOPT ) iopt [ niopt++ ] = optarg ;
else err = msg ( "E2too many modem init commands");
break ;
case 'z':
if ( nzopt < MAXIOPT ) zopt [ nzopt++ ] = optarg ;
else err = msg ( "E2too many modem reset commands");
break ;
case 'd': faxfile = optarg ; break ;
case 'g': getty = optarg ; break ;
case 'o':
for ( ; *optarg ; optarg++ )
switch ( *optarg ) {
case '1' : c1 = 1 ; break ;
case 'a' : softadans = 1 ; break ;
case 'e' : igniniterr = igniniterr ? 0 : 1 ; break ;
case 'r' : rxbitorder = reversebits ; break ;
case 'x' : startchar = XON ; break ;
case 'z' : cmdpause += T_CMD ; break ;
default : msg ( "Wunrecognized protocol option (%c)", *optarg ) ;
}
break ;
case 'q':
if ( sscanf ( optarg , "%d", &maxpgerr ) != 1 )
err=msg ("E2can't read quality (-q) argument (%s)", optarg ) ;
break;
case 'r':
fnamepat = optarg ;
err = receive ( faxfile ) ;
break;
case 's': share = 1 ; break;
case 't':
fnames = argv + optind ;
nfiles = argc - optind ;
err = send ( optarg , faxfile ) ;
break;
case 'v':
verb = optarg ;
if ( strchr ( verb , 'a' ) )
for ( i=0 ; i<argc ; i++ ) msg ( "Iargv[%d]=%s", i, argv[i]) ;
break ;
case 'w': waitforcall = 1 ; break ;
case 'x':
if ( nlocks < MAXLKFILE ) lkfiles [ nlocks++ ] = optarg ;
else err = msg ( "E2too many lock files" ) ;
break ;
case 'T': /* test: begin+end session */
if ( ( err = begin_session ( faxfile ) ) == 0 ) end_session( ) ;
break ;
default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ;
}
}
msg ( "Idone, returning %d", err ) ;
return err ;
}